/**
 * @class ActiveDevices 活跃设备模型 - 每日跑批合并，仅添加本周/本月首次访问的设备。
 */
const BaseMod = require('../base');
const Platform = require('../platform');
const Channel = require('../channel');
const Version = require('../version');

const { DateTime, UniCrypto } = require('../../lib');

const dao = require('./dao');

let db = uniCloud.database();
let _ = db.command;
let $ = _.aggregate;

module.exports = class PayResult extends BaseMod {
    constructor() {
        super();
        this.platforms = [];
        this.channels = [];
        this.versions = [];
    }

    /**
		支付金额：统计时间内，成功支付的订单金额之和（不剔除退款订单）。
		支付笔数：统计时间内，成功支付的订单数，一个订单对应唯一一个订单号。（不剔除退款订单。）
		支付人数：统计时间内，成功支付的人数（不剔除退款订单）。
		支付设备数：统计时间内，成功支付的设备数（不剔除退款订单）。
		下单金额：统计时间内，成功下单的订单金额（不剔除退款订单）。
		下单笔数：统计时间内，成功下单的订单笔数（不剔除退款订单）。
		下单人数：统计时间内，成功下单的客户数，一人多次下单记为一人（不剔除退款订单）。
		下单设备数：统计时间内，成功下单的设备数，一台设备多次访问被计为一台（不剔除退款订单）。
		访问人数：统计时间内，访问人数，一人多次访问被计为一人（只统计已登录的用户）。
		访问设备数：统计时间内，访问设备数，一台设备多次访问被计为一台（包含未登录的用户）。
	 * @desc 支付统计（按日统计）
	 * @param {string} type 统计范围 hour：按小时统计，day：按天统计，week：按周统计，month：按月统计 quarter：按季度统计 year：按年统计
	 * @param {date|time} date
	 * @param {bool} reset
	 */
    async stat(type, date, reset, config = {}) {
        if (!date) date = Date.now();

        // 以下是测试代码-----------------------------------------------------------
        //reset = true;
        //date = 1667318400000;
        // 以上是测试代码-----------------------------------------------------------
        let res = await this.run(type, date, reset, config); // 每小时
        if (type === 'hour' && config.timely) {
            /**
             * 如果是小时纬度统计，则还需要再统计（今日实时数据）
             * 2022-11-01 01:00:00 统计的是 2022-11-01 的天维度数据（即该天0点-1点数据）
             * 2022-11-01 02:00:00 统计的是 2022-11-01 的天维度数据（即该天0点-2点数据）
             * 2022-11-01 23:00:00 统计的是 2022-11-01 的天维度数据（即该天0点-23点数据）
             * 2022-11-02 00:00:00 统计的是 2022-11-01 的天维度数据（即该天最终数据）
             * 2022-11-02 01:00:00 统计的是 2022-11-02 的天维度数据（即该天0点-1点数据）
             */
            date -= 1000 * 3600; // 需要减去1小时
            let tasks = [];
            tasks.push(this.run('day', date, true, 0)); // 今日
            // 以下数据每6小时刷新一次
            const dateTime = new DateTime();
            const timeInfo = dateTime.getTimeInfo(date);
            if ((timeInfo.nHour + 1) % 6 === 0) {
                tasks.push(this.run('week', date, true, 0)); // 本周
                tasks.push(this.run('month', date, true, 0)); // 本月
                tasks.push(this.run('quarter', date, true, 0)); // 本季度
                tasks.push(this.run('year', date, true, 0)); // 本年度
            }
            await Promise.all(tasks);
        }
        return res;
    }
    /**
     * @desc 支付统计
     * @param {string} type 统计范围 hour：按小时统计，day：按天统计，week：按周统计，month：按月统计 quarter：按季度统计 year：按年统计
     * @param {date|time} date 哪个时间节点计算（默认已当前时间计算）
     * @param {bool} reset 如果统计数据已存在，是否需要重新统计
     */
    async run(type, date, reset, offset = -1) {
        let dimension = type;
        const dateTime = new DateTime();
        // 获取统计的起始时间和截止时间
        const dateDimension = dateTime.getTimeDimensionByType(dimension, offset, date);
        let start_time = dateDimension.startTime;
        let end_time = dateDimension.endTime;

        let runStartTime = Date.now();
        let debug = true;
        if (debug) {
            console.log(`-----------------支付统计开始（${dimension}）-----------------`);
            console.log(
                '本次统计时间：',
                dateTime.getDate('Y-m-d H:i:s', start_time),
                '-',
                dateTime.getDate('Y-m-d H:i:s', end_time)
            );
            console.log('本次统计参数：', 'type:' + type, 'date:' + date, 'reset:' + reset);
        }
        this.startTime = start_time;
        let pubWhere = {
            start_time,
            end_time,
        };
        // 查看当前时间段数据是否已存在,防止重复生成

        if (!reset) {
            let list = await dao.uniStatPayResult.list({
                whereJson: {
                    ...pubWhere,
                    dimension,
                },
            });
            if (list.length > 0) {
                console.log('data have exists');
                if (debug) {
                    let runEndTime = Date.now();
                    console.log(`耗时：${((runEndTime - runStartTime) / 1000).toFixed(3)} 秒`);
                    console.log(`-----------------支付统计结束（${dimension}）-----------------`);
                }
                return {
                    code: 1003,
                    msg: 'Pay data in this time have already existed',
                };
            }
        } else {
            let delRes = await dao.uniStatPayResult.del({
                whereJson: {
                    ...pubWhere,
                    dimension,
                },
            });
            console.log('Delete old data result:', JSON.stringify(delRes));
        }
        // 支付订单分组（已下单）
        let statPayOrdersList1 = await dao.uniPayOrders.group({
            ...pubWhere,
            status: '已下单',
        });
        // 支付订单分组（且已付款，含退款）
        let statPayOrdersList2 = await dao.uniPayOrders.group({
            ...pubWhere,
            status: '已付款',
        });
        // 支付订单分组（已退款）
        let statPayOrdersList3 = await dao.uniPayOrders.group({
            ...pubWhere,
            status: '已退款',
        });
        let statPayOrdersList = statPayOrdersList1.concat(statPayOrdersList2).concat(statPayOrdersList3);
        let res = {
            code: 0,
            msg: 'success',
        };
        // 将支付订单分组查询结果组装
        let statDta = {};
        if (statPayOrdersList.length > 0) {
            for (let i = 0; i < statPayOrdersList.length; i++) {
                let item = statPayOrdersList[i];
                let { appid, version, platform, channel } = item._id;
                let { status_str } = item;
                let key = `${appid}-${version}-${platform}-${channel}`;
                if (!statDta[key]) {
                    statDta[key] = {
                        appid,
                        version,
                        platform,
                        channel,
                        status: {},
                    };
                }
                let newItem = JSON.parse(JSON.stringify(item));
                delete newItem._id;
                statDta[key].status[status_str] = newItem;
            }
        }
        if (this.debug) console.log('statDta: ', statDta);

        let saveList = [];
        for (let key in statDta) {
            let item = statDta[key];
            let { appid, version, platform, channel, status: statusData } = item;
            if (!channel) channel = item.scene;

            let fieldData = {
                pay_total_amount: 0,
                pay_order_count: 0,
                pay_user_count: 0,
                pay_device_count: 0,
                create_total_amount: 0,
                create_order_count: 0,
                create_user_count: 0,
                create_device_count: 0,
                refund_total_amount: 0,
                refund_order_count: 0,
                refund_user_count: 0,
                refund_device_count: 0,
            };

            for (let status in statusData) {
                let statusItem = statusData[status];
                if (status === '已下单') {
                    // 已下单
                    fieldData.create_total_amount += statusItem.total_fee;
                    fieldData.create_order_count += statusItem.order_count;
                    fieldData.create_user_count += statusItem.user_count;
                    fieldData.create_device_count += statusItem.device_count;
                } else if (status === '已付款') {
                    // 已付款
                    fieldData.pay_total_amount += statusItem.total_fee;
                    fieldData.pay_order_count += statusItem.order_count;
                    fieldData.pay_user_count += statusItem.user_count;
                    fieldData.pay_device_count += statusItem.device_count;
                } else if (status === '已退款') {
                    // 已退款
                    fieldData.refund_total_amount += statusItem.total_fee;
                    fieldData.refund_order_count += statusItem.order_count;
                    fieldData.refund_user_count += statusItem.user_count;
                    fieldData.refund_device_count += statusItem.device_count;
                }
            }
            // 平台信息
            let platformInfo = null;
            if (this.platforms && this.platforms[platform]) {
                // 从缓存中读取数据
                platformInfo = this.platforms[platform];
            } else {
                const platformObj = new Platform();
                platformInfo = await platformObj.getPlatformAndCreate(platform, null);
                if (!platformInfo || platformInfo.length === 0) {
                    platformInfo._id = '';
                }
                this.platforms[platform] = platformInfo;
            }
            // 渠道信息
            let channelInfo = null;
            const channelKey = appid + '_' + platformInfo._id + '_' + channel;
            if (this.channels && this.channels[channelKey]) {
                channelInfo = this.channels[channelKey];
            } else {
                const channelObj = new Channel();
                channelInfo = await channelObj.getChannelAndCreate(appid, platformInfo._id, channel);
                if (!channelInfo || channelInfo.length === 0) {
                    channelInfo._id = '';
                }
                this.channels[channelKey] = channelInfo;
            }
            // 版本信息
            let versionInfo = null;
            const versionKey = appid + '_' + platform + '_' + version;
            if (this.versions && this.versions[versionKey]) {
                versionInfo = this.versions[versionKey];
            } else {
                const versionObj = new Version();
                versionInfo = await versionObj.getVersionAndCreate(appid, platform, version);
                if (!versionInfo || versionInfo.length === 0) {
                    versionInfo._id = '';
                }
                this.versions[versionKey] = versionInfo;
            }
            let countWhereJson = {
                create_time: _.gte(start_time).lte(end_time),
                appid,
                version,
                platform: _.in(getUniPlatform(platform)),
                channel,
            };
            // 活跃设备数量
            let activity_device_count = await dao.uniStatSessionLogs.groupCount(countWhereJson);
            // 活跃用户数量
            let activity_user_count = await dao.uniStatUserSessionLogs.groupCount(countWhereJson);

            /*
			// TODO 此处有问题，暂不使用
			// 新设备数量
			let new_device_count = await dao.uniStatSessionLogs.groupCount({
				...countWhereJson,
				is_first_visit: 1,
			});
			// 新注册用户数量
			let new_user_count = await dao.uniIdUsers.count({
				register_date: _.gte(start_time).lte(end_time),
				register_env: {
					appid,
					app_version: version,
					uni_platform: _.in(getUniPlatform(platform)),
					channel,
				}
			});


			// 新注册用户中下单的人数
			let new_user_create_order_count = await dao.uniIdUsers.countNewUserOrder({
				whereJson: {
					register_date: _.gte(start_time).lte(end_time),
					register_env: {
						appid,
						app_version: version,
						uni_platform: _.in(getUniPlatform(platform)),
						channel,
					}
				},
				status: [-1, 0]
			});
			// 新注册用户中支付成功的人数
			let new_user_pay_order_count = await dao.uniIdUsers.countNewUserOrder({
				whereJson: {
					register_date: _.gte(start_time).lte(end_time),
					register_env: {
						appid,
						app_version: version,
						uni_platform: _.in(getUniPlatform(platform)),
						channel,
					}
				},
				status: [1, 2, 3]
			}); */

            saveList.push({
                appid,
                platform_id: platformInfo._id,
                channel_id: channelInfo._id,
                version_id: versionInfo._id,
                dimension,
                create_date: Date.now(), // 记录下当前时间
                start_time,
                end_time,
                stat_date: getNowDate(start_time, 8, dimension),
                ...fieldData,
                activity_user_count,
                activity_device_count,
                // new_user_count,
                // new_device_count,
                // new_user_create_order_count,
                // new_user_pay_order_count,
            });
        }
        if (this.debug) console.log('saveList: ', saveList);
        //return;
        if (saveList.length > 0) {
            res = await dao.uniStatPayResult.adds(saveList);
        }
        if (debug) {
            let runEndTime = Date.now();
            console.log(`耗时：${((runEndTime - runStartTime) / 1000).toFixed(3)} 秒`);
            console.log(`本次共添加：${saveList.length} 条记录`);
            console.log(`-----------------支付统计结束（${dimension}）-----------------`);
        }
        return res;
    }
};

function getUniPlatform(platform) {
    let list = [];
    if (['h5', 'web'].indexOf(platform) > -1) {
        list = ['h5', 'web'];
    } else if (['app-plus', 'app'].indexOf(platform) > -1) {
        list = ['app-plus', 'app'];
    } else {
        list = [platform];
    }
    return list;
}

function getNowDate(date = new Date(), targetTimezone = 8, dimension) {
    if (typeof date === 'string' && !isNaN(date)) date = Number(date);
    if (typeof date == 'number') {
        if (date.toString().length == 10) date *= 1000;
        date = new Date(date);
    }
    const { year, month, day, hour, minute, second } = getFullTime(date);
    // 现在的时间
    let date_str;
    if (dimension === 'month') {
        date_str = timeFormat(date, 'yyyy-MM', targetTimezone);
    } else if (dimension === 'quarter') {
        date_str = timeFormat(date, 'yyyy-MM', targetTimezone);
    } else if (dimension === 'year') {
        date_str = timeFormat(date, 'yyyy', targetTimezone);
    } else {
        date_str = timeFormat(date, 'yyyy-MM-dd', targetTimezone);
    }
    return {
        date_str,
        year,
        month,
        day,
        hour,
        //minute,
        //second,
    };
}

function getFullTime(date = new Date(), targetTimezone = 8) {
    if (!date) {
        return '';
    }
    if (typeof date === 'string' && !isNaN(date)) date = Number(date);
    if (typeof date == 'number') {
        if (date.toString().length == 10) date *= 1000;
        date = new Date(date);
    }
    const dif = date.getTimezoneOffset();
    const timeDif = dif * 60 * 1000 + targetTimezone * 60 * 60 * 1000;
    const east8time = date.getTime() + timeDif;
    date = new Date(east8time);

    let YYYY = date.getFullYear() + '';
    let MM = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
    let DD = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
    let hh = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
    let mm = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
    let ss = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
    return {
        YYYY: Number(YYYY),
        MM: Number(MM),
        DD: Number(DD),
        hh: Number(hh),
        mm: Number(mm),
        ss: Number(ss),

        year: Number(YYYY),
        month: Number(MM),
        day: Number(DD),
        hour: Number(hh),
        minute: Number(mm),
        second: Number(ss),
    };
}

/**
 * 日期格式化
 */
function timeFormat(time, fmt = 'yyyy-MM-dd hh:mm:ss', targetTimezone = 8) {
    try {
        if (!time) {
            return '';
        }
        if (typeof time === 'string' && !isNaN(time)) time = Number(time);
        // 其他更多是格式化有如下:
        // yyyy-MM-dd hh:mm:ss|yyyy年MM月dd日 hh时MM分等,可自定义组合
        let date;
        if (typeof time === 'number') {
            if (time.toString().length == 10) time *= 1000;
            date = new Date(time);
        } else {
            date = time;
        }

        const dif = date.getTimezoneOffset();
        const timeDif = dif * 60 * 1000 + targetTimezone * 60 * 60 * 1000;
        const east8time = date.getTime() + timeDif;

        date = new Date(east8time);
        let opt = {
            'M+': date.getMonth() + 1, //月份
            'd+': date.getDate(), //日
            'h+': date.getHours(), //小时
            'm+': date.getMinutes(), //分
            's+': date.getSeconds(), //秒
            'q+': Math.floor((date.getMonth() + 3) / 3), //季度
            S: date.getMilliseconds(), //毫秒
        };
        if (/(y+)/.test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
        }
        for (let k in opt) {
            if (new RegExp('(' + k + ')').test(fmt)) {
                fmt = fmt.replace(
                    RegExp.$1,
                    RegExp.$1.length == 1 ? opt[k] : ('00' + opt[k]).substr(('' + opt[k]).length)
                );
            }
        }
        return fmt;
    } catch (err) {
        // 若格式错误,则原值显示
        return time;
    }
}
